package tech.dubbo.trigger.socket;

import com.alibaba.fastjson.JSON;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import tech.dubbo.api.dto.UserReqDTO;
import tech.dubbo.api.dto.UserResDTO;
import tech.dubbo.api.types.Request;
import tech.dubbo.api.types.Response;
import tech.dubbo.trigger.code.MapDecoder;
import tech.dubbo.trigger.code.MapEncoder;
import tech.dubbo.trigger.codec.JsonDecoder;
import tech.dubbo.trigger.codec.JsonEncoder;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName
 * @Author niujie
 * @Version
 * @Description
 * @CreateTime 2024/9/19
 */
@Slf4j
@Service
public class RpcServerSocket implements Runnable{

    private ApplicationContext applicationContext;

    public RpcServerSocket(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        new Thread(this).start();
    }


    @Override
    public void run() {

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel channel) {

                            channel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                            channel.pipeline().addLast(new JsonDecoder<>(UserReqDTO.class));
                            channel.pipeline().addLast(new JsonEncoder());
                            channel.pipeline().addLast(new SimpleChannelInboundHandler<Request>() {

                                @Override
                                protected void channelRead0(ChannelHandlerContext channelHandlerContext, Request request) throws Exception {
                                    // 解析参数
                                    Map<String, Object> data = (Map<String, Object>) request.getData();
                                    Class<?> clazz = Class.forName((String)data.get("clazz"));
                                    String methodName = (String) data.get("methodName");
                                    List<String> types = (List<String>) data.get("paramTypes");
                                    Class<?>[] paramTypes = types.stream().map(type -> {
                                        try {
                                            return Class.forName(type);
                                        } catch (ClassNotFoundException e) {
                                            e.printStackTrace();
                                            return UserResDTO.class;
                                        }
                                    }).toArray(Class<?>[]::new);
                                    Object[] args = request.getArgs();

                                    // 反射调用
                                    Method method = clazz.getMethod(methodName, paramTypes);
                                    Response<UserResDTO> invoke = (Response<UserResDTO>)method.invoke(applicationContext.getBean("userService"), args);

                                    invoke.setSerializeId(request.getSerializeId());
                                    // 回写数据
                                    ChannelFuture future = channelHandlerContext.channel().writeAndFlush(invoke);
                                    future.addListener((ChannelFuture cf)  -> {
                                       if (cf.isSuccess()) {
                                           System.out.println("success");
                                       }else {
                                           System.out.println("error");
                                       }
                                    });
                                }

                                @Override
                                public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

                                    System.out.println(cause + "1111");
                                }
                            });
                        }
                    });

            ChannelFuture f = b.bind(8085).sync();
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}
